use crate::common::*;
use futures_util::stream::StreamExt;
use soketto::connection::Sender;
use soketto::handshake::{server::Response, Server as WsServer};
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
use unmp_link::prelude::*;

struct Conn {
    identifier: String,
    tx: Sender<Compat<TcpStream>>,
    rx: Rx,
}
#[async_trait]
impl ConnectionDriver for Conn {
    fn is_subnet(&self) -> bool {
        true
    }
    fn kind(&self) -> &str {
        "ws_server"
    }
    fn id(&self) -> &str {
        &self.identifier
    }
    async fn send(&mut self, buf: Bytes) -> Result<(), SendError> {
        if let Err(_) = self.tx.send_binary(&buf).await {
            return Err(SendError::Disconnected);
        }
        Ok(())
    }
    async fn recv(&mut self) -> Result<Bytes, RecvError> {
        loop {
            let (data_type, data) = match self.rx.next().await {
                Some(Ok(data_type)) => data_type,
                _ => return Err(RecvError::Disconnected),
            };
            if data_type.is_binary() {
                return Ok(data.into());
            }
        }
    }
}

pub struct Server {
    identifier: String,
    socket: TcpListener,
}
impl Server {
    pub async fn new(addr: SocketAddr) -> Result<Listener, ()> {
        let identifier = addr.to_string();
        let socket = TcpListener::bind(addr).await.map_err(|_| ())?;
        Ok(Listener::new(Box::new(Self { identifier, socket })))
    }
}
#[async_trait]
impl ListenerDriver for Server {
    fn kind(&self) -> &str {
        "ws_server"
    }
    fn id(&self) -> &str {
        &self.identifier
    }
    async fn accept(&mut self) -> Result<Connection, RecvError> {
        loop {
            let (socket, addr) = match self.socket.accept().await {
                Ok(d) => d,
                Err(err) => {
                    log::error!("todo: {:?}", err);
                    return Err(RecvError::Disconnected);
                }
            };

            let mut conn = WsServer::new(socket.compat());
            conn.add_protocol(PROTOCOL);
            let rsp = if let Ok(req) = conn.receive_request().await {
                if req.path() == PATH {
                    Response::Accept {
                        key: req.key(),
                        protocol: Some(PROTOCOL),
                    }
                } else {
                    Response::Reject { status_code: 404 }
                }
            } else {
                continue;
            };
            if conn.send_response(&rsp).await.is_err() {
                continue;
            }
            if let Response::Reject { .. } = rsp {
                continue;
            }

            let (tx, rx) = conn.into_builder().finish();
            let identifier = addr.to_string();
            return Ok(Connection::new(Box::new(Conn {
                identifier,
                tx,
                rx: convert_rx(rx),
            })));
        }
    }
}
